#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/wait.h>
#include <linux/ide.h>
#include <linux/poll.h>

#define KEY_COUNT 1
#define KEY_NAME "irq"

#define KEYVALUE 0X01

/* 按键结构体 */
struct key_dev_t
{
    int gpio;                            /* io编号 */
    int irqnum;                          /* 中断号 */
    unsigned char value;                 /* 键值 */
    irqreturn_t (*handler)(int, void *); /* 中断处理函数 */
    unsigned char release;               /* 按键松开 */
};

/* 设备结构体 */
struct irq_dev_t
{
    dev_t devid;                   /* 设备号 */
    int major;                     /* 主设备号 */
    int minor;                     /* 次设备号 */
    struct cdev cdev;              /* 字符设备 */
    struct class *class;           /* 类 */
    struct device *device;         /* 设备 */
    struct device_node *nd;        /* 节点 */
    struct key_dev_t key_dev;      /* 按键设备 */
    struct timer_list timer;       /* 定时器 */
    struct tasklet_struct tasklet; /* 定义tasklet */
    wait_queue_head_t r_wait;      /* 定义读等待队列头 */
};
struct irq_dev_t irq_dev; /* key设备 */

static int irq_open(struct inode *inode, struct file *filp)
{
    // filp->private_data = &irq_dev;

    return 0;
}

static ssize_t irq_release(struct inode *inode, struct file *filp)
{
    // struct dtsirq_dev *dev = (struct dtsirq_dev *)filp->private_data;
    return 0;
}

static ssize_t irq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    int ret = 0;
    unsigned char keyvalue;
    unsigned char keyrelease;

    if (filp->f_flags & O_NONBLOCK)
    {
        if (irq_dev.key_dev.release == 0) /* 非阻塞 */
            return -EAGAIN;
    }
    else
    {
        /* 等待事件 */
        wait_event_interruptible(irq_dev.r_wait, irq_dev.key_dev.release);
    }

    keyvalue = irq_dev.key_dev.value;
    keyrelease = irq_dev.key_dev.release;

    if (keyrelease)
    {
        ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
        irq_dev.key_dev.release = 0;
    }
    else
        return -EINVAL;

    return ret;
}

unsigned int irq_poll(struct file *filp, struct poll_table_struct *wait)
{
    int mask = 0;

    poll_wait(filp, &irq_dev.r_wait, wait);

    /* 判断是否可读 */
    if (irq_dev.key_dev.release) /* 按键按下 */
    {
        mask = POLLIN | POLLRDNORM; /* 返回POLLIN */
    }

    return mask;
}

/* 字符设备操作集合 */
static const struct file_operations irq_fops = {
    .owner = THIS_MODULE,
    .open = irq_open,
    .read = irq_read,
    .release = irq_release,
    .poll = irq_poll,
};

/* 定时器处理函数 */
static void timer_handler(unsigned long arg)
{
    int value = 0;
    struct irq_dev_t *dev = (struct irq_dev_t *)arg;

    value = gpio_get_value(dev->key_dev.gpio);
    if (value == 0)
        irq_dev.key_dev.value = KEYVALUE;
    else
    {
        irq_dev.key_dev.release = 1;
    }

    /* 唤醒进程 */
    if (irq_dev.key_dev.release)
    {
        wake_up_interruptible(&irq_dev.r_wait);
    }
}
/* 中断处理函数 */
static irqreturn_t key_handler(int irq, void *devid)
{
    struct irq_dev_t *dev = (struct irq_dev_t *)devid;

    /* 调度tasklet */
    tasklet_schedule(&dev->tasklet);

    return IRQ_HANDLED;
}

/* tasklet处理函数 */
static void key_tasklet(unsigned long data)
{
    struct irq_dev_t *dev = (struct irq_dev_t *)data;

    dev->timer.data = (unsigned long)data;
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(20));
}

static int key0_init(struct irq_dev_t *dev)
{
    int ret = 0;
    /* 获取设备节点 */
    dev->nd = of_find_node_by_path("/key");
    if (dev->nd == NULL)
    {
        ret = -EINVAL;
        goto fail_find_node;
    }

    /* 获取key所对应的gpio */
    dev->key_dev.gpio = of_get_named_gpio(dev->nd, "key-gpios", 0);
    if (dev->key_dev.gpio < 0)
    {
        printk("can't find key gpio \r\n");
        ret = -EINVAL;
        goto fail_find_gpio;
    }
    printk("key number = %d \r\n", dev->key_dev.gpio);

    /* 申请gpio */
    ret = gpio_request(dev->key_dev.gpio, "key");
    if (ret)
    {
        printk("fail request gpio \r\n");
        ret = -EBUSY;
        goto fail_request;
    }

    /* 使用gpio,设置为输入 */
    ret = gpio_direction_input(dev->key_dev.gpio);
    if (ret < 0)
    {
        printk("fail set iuput \r\n");
        ret = -EINVAL;
        goto fail_set_gpio;
    }

    /* 获取中断编号 */
    dev->key_dev.irqnum = gpio_to_irq(dev->key_dev.gpio);

    /* 初始化中断 */
    irq_dev.key_dev.handler = key_handler;
    irq_dev.key_dev.value = KEYVALUE;
    ret = request_irq(dev->key_dev.irqnum, irq_dev.key_dev.handler,
                      IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "key", &irq_dev);
    if (ret)
    {
        printk("fail request irq \r\n");
        ret = -EINVAL;
        goto fail_request_irq;
    }

    /* 初始化tasklet */
    tasklet_init(&irq_dev.tasklet, key_tasklet, (unsigned long)dev);

    /* 初始化定时器 */
    init_timer(&irq_dev.timer);
    irq_dev.timer.function = timer_handler;
    irq_dev.timer.data = (unsigned long)&irq_dev;

    return 0;

fail_request_irq:
fail_set_gpio:
    gpio_free(dev->key_dev.gpio);
fail_request:
fail_find_gpio:
fail_find_node:
    return ret;
}

/* 入口 */
static int __init key_irq_init(void)
{
    int ret = 0;
    printk("irq driver init\r\n");

    /* 申请设备号 */
    irq_dev.major = 0; /* 设备号由内核分配 */
    if (irq_dev.major) /* 给定了设备号 */
    {
        irq_dev.devid = MKDEV(irq_dev.major, 0);
        ret = register_chrdev_region(irq_dev.devid, KEY_COUNT, KEY_NAME);
    }
    else /* 没有给定设备号 */
    {
        ret = alloc_chrdev_region(&irq_dev.devid, 0, KEY_COUNT, KEY_NAME);
        irq_dev.major = MAJOR(irq_dev.devid);
        irq_dev.minor = MAJOR(irq_dev.devid);
        printk("major = %d minor = %d \r\n", irq_dev.major, irq_dev.minor);
    }
    if (ret < 0)
    {
        goto fail_devid;
    }

    /* 添加字符设备 */
    irq_dev.cdev.owner = THIS_MODULE;
    cdev_init(&irq_dev.cdev, &irq_fops);
    ret = cdev_add(&irq_dev.cdev, irq_dev.devid, KEY_COUNT);
    if (ret < 0)
    {
        goto fail_cdev;
    }

    /* 自动创建设备节点 */
    irq_dev.class = class_create(THIS_MODULE, KEY_NAME);
    if (IS_ERR(irq_dev.class))
    {
        ret = PTR_ERR(irq_dev.class);
        goto fail_class;
    }
    irq_dev.device = device_create(irq_dev.class, NULL, irq_dev.devid, NULL, KEY_NAME);
    if (IS_ERR(irq_dev.device))
    {
        ret = PTR_ERR(irq_dev.device);
        goto fail_device;
    }

    /* 按键初始化 */
    ret = key0_init(&irq_dev);
    if (ret < 0)
    {
        goto fail_key_init;
    }

    /* 初始化等待队列头 */
    init_waitqueue_head(&irq_dev.r_wait);

    return 0;

fail_key_init:
fail_device:
    class_destroy(irq_dev.class);
fail_class:
    cdev_del(&irq_dev.cdev);
fail_cdev:
    unregister_chrdev_region(irq_dev.devid, KEY_COUNT);
fail_devid:
    return ret;
}

/* 出口 */
static void __exit key_irq_exit(void)
{
    /* 释放中断 */
    free_irq(irq_dev.key_dev.irqnum, &irq_dev);
    /* 删除定时器 */
    del_timer_sync(&irq_dev.timer);
    /* 释放gpio */
    gpio_free(irq_dev.key_dev.gpio);
    /* 删除字符设备驱动 */
    cdev_del(&irq_dev.cdev);
    /* 释放设备号 */
    unregister_chrdev_region(irq_dev.devid, KEY_COUNT);
    /* 摧毁设备 */
    device_destroy(irq_dev.class, irq_dev.devid);
    /* 摧毁类 */
    class_destroy(irq_dev.class);

    printk("irq driver exit \r\n");
}

/* 注册驱动的加载与卸载 */
module_init(key_irq_init);
module_exit(key_irq_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("yuzhou");
